#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#include "gm_sm3.h"
#include "gm_sm4.h"
#include "sm3_new.h"
#include "sdt_sm4.h"
#include "sdt_sm3.h"
#include "sdt_sm2.h"
#include "sdt_sm.h"
#include <string.h>
#include <openssl/opensslconf.h>
#include "ossl_sm4.h"

static int test_sm4_ecb(void)
{
    static const uint8_t k[SM4_BLOCK_SIZE] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10
    };

    static const uint8_t input[SM4_BLOCK_SIZE] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10
    };

    /*
     * This test vector comes from Example 1 of GB/T 32907-2016,
     * and described in Internet Draft draft-ribose-cfrg-sm4-02.
     */
    static const uint8_t expected[SM4_BLOCK_SIZE] = {
        0x68, 0x1e, 0xdf, 0x34, 0xd2, 0x06, 0x96, 0x5e,
        0x86, 0xb3, 0xe9, 0x4f, 0x53, 0x6e, 0x42, 0x46
    };

    /*
     * This test vector comes from Example 2 from GB/T 32907-2016,
     * and described in Internet Draft draft-ribose-cfrg-sm4-02.
     * After 1,000,000 iterations.
     */
    static const uint8_t expected_iter[SM4_BLOCK_SIZE] = {
        0x59, 0x52, 0x98, 0xc7, 0xc6, 0xfd, 0x27, 0x1f,
        0x04, 0x02, 0xf8, 0x04, 0xc3, 0x3d, 0x3f, 0x66
    };

    int i;
    SM4_KEY key;
    uint8_t block[SM4_BLOCK_SIZE];

    ossl_sm4_set_key(k, &key);
    memcpy(block, input, SM4_BLOCK_SIZE);

    ossl_sm4_encrypt(block, block, &key);

    for (i = 0; i != 999999; ++i)
        ossl_sm4_encrypt(block, block, &key);

    for (i = 0; i != 1000000; ++i)
        ossl_sm4_decrypt(block, block, &key);

    return 1;
}

static uint8_t spa[64] = {
	0x09, 0xf9, 0xdf, 0x31, 0x1e, 0x54, 0x21, 0xa1, 0x50, 0xdd, 0x7d, 0x16, 0x1e, 0x4b, 0xc5, 0xc6,
	0x72, 0x17, 0x9f, 0xad, 0x18, 0x33, 0xfc, 0x07, 0x6b, 0xb0, 0x8f, 0xf3, 0x56, 0xf3, 0x50, 0x20,
	0xcc, 0xea, 0x49, 0x0c, 0xe2, 0x67, 0x75, 0xa5, 0x2d, 0xc6, 0xea, 0x71, 0x8c, 0xc1, 0xaa, 0x60,
	0x0a, 0xed, 0x05, 0xfb, 0xf3, 0x5e, 0x08, 0x4a, 0x66, 0x32, 0xf6, 0x07, 0x2d, 0xa9, 0xad, 0x13,
};

static uint8_t td[34] = {
	0xf5, 0xa0, 0x3b, 0x06, 0x48, 0xd2, 0xc4 ,0x63, 0x0e, 0xea, 0xc5, 0x13, 0xe1, 0xbb, 0x81, 0xa1,
	0x59, 0x44, 0xda, 0x38, 0x27, 0xd5, 0xb7, 0x41, 0x43, 0xac, 0x7e, 0xac, 0xee, 0xe7, 0x20, 0xb3,
	0x11, 0x22,
};

static int32_t show_hex(int8_t* title, void* buf, uint32_t total)
{
	uint32_t idx;
	uint8_t* data = (uint8_t *)buf;

	printf("%s(%d):\n\t", title, total);
	for (idx = 0; idx < total; idx++) {
		printf("%02hhx", data[idx]);
		printf("%s", 15 == (idx & 15) ? "\n\t" : ", ");
	}
	printf("\n");
	
	return 0;
}

static int32_t test_sm4(int32_t argc, int8_t** argv)
{
	uint8_t cipher[512];
	uint8_t buf[512];
	int32_t len;
	uint32_t type = 0;
	sm4_ctx_t sm4;

	if (argc) {
		type = strtol(argv[0], NULL, 10);
	}

	memset(cipher, 0, sizeof(cipher));
	memset(buf, 0, sizeof(buf));
	memset(&sm4, 0, sizeof(sm4));

	switch (type) {
	case 1:
		printf("sm4 cbc test:\n");
		break;
	case 2:
		printf("sm4 cfb test:\n");
		break;
	case 3:
		printf("sm4 ofb test:\n");
		break;
	case 4:
		printf("sm4 ctr test:\n");
		break;
	default:
		printf("sm4 ecb test:\n");
		type = 0;
		break;
	}
	show_hex("data", td, sizeof(td));

	gm_sm4_create(type);
	gm_sm4_enckey(&sm4, spa);
	memcpy(sm4.iv, spa + 16, 16);

	if ((len = gm_sm4_encrypt(&sm4, sizeof(td), td, cipher)) < 0) {
		return -1;
	}
	show_hex("encode", cipher, len);

	gm_sm4_deckey(&sm4, spa);
	memcpy(sm4.iv, spa + 16, 16);

	if ((len = gm_sm4_decrypt(&sm4, len, cipher, buf)) < 0) {
		return -1;
	}
	show_hex("decode", buf, len);

	return 0;
}

static int32_t test_sm3(int32_t argc, int8_t** argv)
{
	uint8_t buf[32];

	sm3_proc(td, sizeof(td), buf);
	show_hex("data", td, sizeof(td));
	show_hex("buf", buf, 32);

	return 0;
}

int test_sm4_encrypt(const char * input, const char * key, char ** output)
{
	return 0;	
}

int test_sm4_decrypt(const char * input, const char * key, char ** output)
{
	return 0;	
}

int test_sm3_dgst(const char * input, char * output1)
{
	uint8_t output[32];
	sm3_proc((uint8_t *)input, strlen((const char *)td), output);
	show_hex("data", (uint8_t *)input, strlen((const char *)td));
	show_hex("buf", output, 32);
	return 0;
}

int32_t main(int32_t argc, int8_t** argv)
{
/*
	if (argc < 2) {
		printf("iuput test type: sm3 or sm4\n");
		return -1;
	}

	if (!strcmp(argv[1], "sm3")) {
		return test_sm3(argc - 2, argv + 2);
	}

	if (argc > 2) {
		argc -= 2;
		argv += 2;
	} else {
		argc -= 1;
		argv += 1;
	}

	return test_sm4(argc, argv);
*/	
	test_sm4_ecb();
/*
	char result[32];
	test_sm3_dgst("abc", result);
	const char * in = "abc";
	SM3Calc("abc", strlen(in), result);
	show_hex("result", result, 32);
*/	
    const uint8_t * src =  (const uint8_t * )"abcdefg";
	const uint8_t * mykey =  (const uint8_t * )"abc";
    uint8_t buffer[64];
	memset(buffer, 0, sizeof(buffer));
    SM4_KEY key;
    ossl_sm4_set_key(mykey, &key);

    ossl_sm4_encrypt(src, buffer, &key);
	show_hex("sm4 result", buffer, strlen((const char *)buffer));
    uint8_t result[64];
	ossl_sm4_decrypt(buffer, result, &key);
	printf("%s\n", result);
	
	SM4_EncECB((uint8_t *)mykey, (uint8_t *)src, buffer, strlen((const char *)src));
	show_hex("sm4 enc result", buffer, strlen((const char *)buffer));

    SM4_DecECB((uint8_t *)mykey, buffer, result, strlen((const char *)buffer));
	printf("%s\n", result);

    memset(buffer, 0, sizeof(buffer));
    KDF((uint8_t *)src, strlen((const char *)src), sizeof(buffer), buffer);
	show_hex("kdf result:", buffer, sizeof(buffer));

    // init_random();
    memset(buffer, 0, sizeof(buffer));
    int ret = sdt_random_gen(buffer, 32);
	if (ret == 0)
	{
	    show_hex("random result:", buffer, sizeof(buffer));
	}

    uint8_t private_key[32] = {0};
	uint32_t pv_len = 0;
	KDF((uint8_t *)src, strlen((const char *)src), sizeof(private_key), private_key);
	{
		show_hex("private_key:", private_key, sizeof(private_key));
	}
    uint8_t public_key[64] = {0};
	uint32_t pk_len = sizeof(public_key);
	
	ret = sdt_ecc_makekey(private_key, sizeof(private_key), public_key, &pk_len);
    {
		show_hex("public_key:", public_key, sizeof(public_key));
	}
    uint8_t hash[32] = {0};
	uint32_t hash_len = 32;
	ret = sdt_hash((uint8_t *)src, strlen((const char *)src), hash, &hash_len);
    if (ret == 0)
	{   
		printf("hash len %d\n", hash_len);
		show_hex("hash value", hash, sizeof(hash));
	}
    uint8_t random[32] = {0};
	ret = sdt_random_gen(random, 32);
	if (ret == 0)
	{
	    show_hex("random result:", random, sizeof(random));
	}

    uint8_t sign[64] = {0};
	unsigned int sign_len = sizeof(sign);
	ret = sdt_ecc_sign(hash, 
	                   sizeof(hash), 
					   random, 
					   sizeof(random),
					   private_key,
					   sizeof(private_key),
					   sign, 
					   &sign_len);
    if(ret == 0)
	{
	    show_hex("sign result:", sign, sign_len);
	}

    ret = sdt_ecc_verify(hash, sizeof(hash), public_key, sizeof(public_key), sign, sign_len);
    if(ret == 0)
	{
		printf("sign verify successfully\n");
	}

	uint8_t cipher[256] = {0};
	uint32_t cipher_len = sizeof(cipher);
    ret = sdt_ecc_encrypt((uint8_t *)src, 
	                      strlen((const char *)src),
						  random,
                          sizeof(random),
						  public_key,
						  sizeof(public_key),
						  cipher,
						  &cipher_len
	                     );
	if(ret == 0)
    {
	    show_hex("cipher result:", cipher, cipher_len);
	}					

   memset(buffer, 0, sizeof(buffer));
   unsigned int plain_len = sizeof(buffer);		 	
   ret = sdt_ecc_decrypt(cipher,
                         cipher_len,
						 (uint8_t *)private_key,
						 sizeof(private_key),
						 (uint8_t *)buffer,
						 &plain_len							 
						 );
	if(ret == 0)
    {
	    show_hex("decrypt result:", buffer, plain_len);
		printf("length = %d, result : %s\n", plain_len, buffer);
	}					
    return 0; 
}
